package com.qihetimes.earthangel.service.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qihetimes.earthangel.common.ApiException;
import com.qihetimes.earthangel.common.ConversationType;
import com.qihetimes.earthangel.common.MessageType;
import com.qihetimes.earthangel.mapper.ConversationMapper;
import com.qihetimes.earthangel.model.*;
import com.qihetimes.earthangel.model.bo.ConversationBO;
import com.qihetimes.earthangel.model.bo.ConversationShowBO;
import com.qihetimes.earthangel.model.bo.MessageSendBO;
import com.qihetimes.earthangel.service.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 会话表 服务实现类
 * </p>
 *
 * @author f4cklangzi@gmail.com
 * @since 2021-12-18
 */
@Service
public class ConversationServiceImpl extends ServiceImpl<ConversationMapper, Conversation> implements IConversationService {
    
    @Resource
    private IUserService userService;
    
    @Resource
    private IConversationMessageService conversationMessageService;
    
    @Resource
    private IGoodsService goodsService;
    
    @Resource
    private IGoodsPhotoService goodsPhotoService;
    
    @Resource
    private JPushClientService jPushClientService;
    
    @Resource
    private WxAppMessageService wxAppMessageService;
    
    @Override
    public Page<Conversation> list(ConversationBO param) {
        User loginUser = userService.getLoginUser();
        Page<Conversation> queryPage = new Page<>(param.getPageNum(), param.getPageSize());
        LambdaQueryChainWrapper<Conversation> wrapper = lambdaQuery()
            .eq(Conversation::getToUserId, loginUser.getId())
            .orderByAsc(Conversation::getType)
            .orderByDesc(Conversation::getUpdatedAt);
        if (param.getConversationType() != null) {
            wrapper.eq(Conversation::getType, param.getConversationType());
        }
        wrapper.page(queryPage);
        return queryPage;
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Conversation create(String userId, String goodsId) {
        User loginUser = userService.getLoginUser();
        User toUser = userService.getById(userId);
        if (toUser == null) {
            throw new ApiException("用户不存在!");
        }
        String goodsInfo = null;
        if (goodsId != null) {
            Goods goods = goodsService.getById(goodsId);
            if (goods != null) {
                List<GoodsPhoto> photoList = goodsPhotoService.lambdaQuery().eq(GoodsPhoto::getGoodsId, goodsId).list();
                List<String> photoUrlList = photoList.stream().map(GoodsPhoto::getPhotoUrl)
                    .collect(Collectors.toList());
                JSONObject object = new JSONObject();
                object.set("goodsId", goods.getId());
                object.set("userId", goods.getUserId());
                object.set("goodsName", goods.getName());
                object.set("goodsPhotoList", photoUrlList);
                goodsInfo = JSONUtil.toJsonStr(object);
            }
        }
        Conversation conversation = lambdaQuery().eq(Conversation::getFromUserId, toUser.getId())
            .eq(Conversation::getToUserId, loginUser.getId())
            .one();
        if (conversation != null) {
            lambdaUpdate().eq(Conversation::getRelationId, conversation.getRelationId())
                .set(Conversation::getGoodsInfo, goodsInfo)
                .update();
            conversation.setGoodsInfo(goodsInfo);
            return conversation;
        }
        return createConversation(loginUser, toUser, goodsInfo);
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void send(MessageSendBO param) {
        String conversationId = param.getConversationId();
        User loginUser = userService.getLoginUser();
        LocalDateTime now = LocalDateTime.now();
        Conversation conversation = getById(conversationId);
        if (conversation == null || !conversation.getToUserId().equals(loginUser.getId())) {
            throw new ApiException("会话不存在!");
        }
        Conversation targetConversation = lambdaQuery()
            .eq(Conversation::getRelationId, conversation.getRelationId())
            .eq(Conversation::getToUserId, conversation.getFromUserId())
            .one();
        ConversationMessage message = new ConversationMessage();
        message.setId(IdUtil.objectId());
        message.setConversationRelationId(conversation.getRelationId());
        message.setUserId(loginUser.getId());
        message.setMessageType(param.getMessageType());
        message.setContent(param.getContent());
        message.setCreatedAt(now);
        conversationMessageService.save(message);
        targetConversation.setUpdatedAt(now);
        targetConversation.setUnreadCount(targetConversation.getUnreadCount() + 1);
        updateById(targetConversation);
        String fromUserId = conversation.getFromUserId();
        User fromUser = userService.getById(fromUserId);
        String registrationId = fromUser.getRegistrationId();
        if (StrUtil.isNotBlank(registrationId)) {
            jPushClientService.sendPush(registrationId, targetConversation, message);
        }
    }
    
    @Override
    public Page<ConversationMessage> show(ConversationShowBO param) {
        String conversationId = param.getConversationId();
        User loginUser = userService.getLoginUser();
        Conversation conversation = getById(conversationId);
        if (!conversation.getToUserId().equals(loginUser.getId())) {
            throw new ApiException("会话不存在!");
        }
        conversation.setUnreadCount(0);
        updateById(conversation);
        Page<ConversationMessage> queryPage = new Page<>(param.getPageNum(), param.getPageSize());
        conversationMessageService.lambdaQuery()
            .eq(ConversationMessage::getConversationRelationId, conversation.getRelationId())
            .orderByDesc(ConversationMessage::getCreatedAt)
            .page(queryPage);
        return queryPage;
    }
    
    @Override
    public Conversation getSystemConversation(User user) {
        Conversation conversation = lambdaQuery()
            .eq(Conversation::getType, ConversationType.SYSTEM)
            .eq(Conversation::getToUserId, user.getId())
            .one();
        if (conversation != null) {
            return conversation;
        }
        conversation = new Conversation();
        conversation.setId(IdUtil.objectId());
        conversation.setType(ConversationType.SYSTEM.name());
        conversation.setRelationId(conversation.getId());
        conversation.setFromUserId("000000000000000000000000");
        conversation.setFromUserNickName("系统消息");
        conversation.setFromUserAvatarUrl("");
        conversation.setToUserId(user.getId());
        conversation.setToUserNickName(user.getNickName());
        conversation.setToUserAvatarUrl(user.getAvatarUrl());
        conversation.setUnreadCount(0);
        conversation.setCreatedAt(LocalDateTime.now());
        conversation.setUpdatedAt(LocalDateTime.now());
        save(conversation);
        return conversation;
    }
    
    @Override
    public void sendSystemMessage(MessageType messageType, User toUser, String content, boolean isPush) {
        Conversation conversation = getSystemConversation(toUser);
        ConversationMessage message = new ConversationMessage();
        message.setId(IdUtil.objectId());
        message.setConversationRelationId(conversation.getRelationId());
        message.setUserId(conversation.getFromUserId());
        message.setContent(content);
        message.setMessageType(messageType.name());
        message.setCreatedAt(LocalDateTime.now());
        conversationMessageService.save(message);
        conversation.setUnreadCount(conversation.getUnreadCount() + 1);
        updateById(conversation);
        String registrationId = toUser.getRegistrationId();
        if (isPush && StrUtil.isNotBlank(registrationId)) {
            jPushClientService.sendPush(registrationId, conversation, message);
        }
        String openid = toUser.getOpenid();
        if (isPush && StrUtil.isNotBlank(openid)) {
            wxAppMessageService.sendPush(openid, message);
        }
    }
    
    /**
     * 创建一个新会话
     *
     * @param fromUser  会话发起人
     * @param toUser    会话对方
     * @param goodsInfo 物品信息JSON
     * @return 发起人作为接收人的会话
     */
    private Conversation createConversation(User fromUser, User toUser, String goodsInfo) {
        ConversationType type = ConversationType.USER;
        if (fromUser.getPhone() == null) {
            type = ConversationType.SYSTEM;
        }
        String relationId = IdUtil.objectId();
        LocalDateTime now = LocalDateTime.now();
        Conversation fromConversation = createConversation(fromUser, toUser, goodsInfo, type, relationId, now);
        Conversation toConversation = createConversation(toUser, fromUser, goodsInfo, type, relationId, now);
        saveBatch(Arrays.asList(fromConversation, toConversation));
        return toConversation;
    }
    
    private Conversation createConversation(User fromUser, User toUser, String goodsInfo, ConversationType type,
        String relationId,
        LocalDateTime now) {
        Conversation conversation = new Conversation();
        conversation.setId(IdUtil.objectId());
        conversation.setType(type.name());
        conversation.setRelationId(relationId);
        conversation.setFromUserId(fromUser.getId());
        conversation.setFromUserNickName(fromUser.getNickName());
        conversation.setFromUserAvatarUrl(fromUser.getAvatarUrl());
        conversation.setToUserId(toUser.getId());
        conversation.setToUserNickName(toUser.getNickName());
        conversation.setToUserAvatarUrl(toUser.getAvatarUrl());
        conversation.setUnreadCount(0);
        conversation.setGoodsInfo(goodsInfo);
        conversation.setCreatedAt(now);
        conversation.setUpdatedAt(now);
        return conversation;
    }
}
